﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harkon.AppServer.cOne.Service;
using log4net;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace Harkon.AppServer.cOne.Cluster
{
    public interface IMulticastReceiver : IService
    {
        void AddMulticastMessageConsumer(IMulticastMessageInterested consumer);
    }

    public class MulticastReceiver : AbstractService, IMulticastReceiver
    {

        public ProtocolHelper Helper { get; set; }

        private static ILog logger = LogManager.GetLogger(typeof(MulticastReceiver));

        public string IpAddressString { get; set; }
        public int PortNumber { get; set; }
        public IMulticastSender Sender { get; set; }

        private UdpClient listener { get; set; }
        private IPAddress CurrentIpAddress { get; set; }

        private Thread ReceiverThread { get; set; }

        private bool Running { get; set; }

        public MulticastReceiver()
        {
            MulticastMessageConsumer = new List<IMulticastMessageInterested>();
        }

        #region life cycle

        public override bool Init()
        {
            if (base.Init())
            {
                logger.InfoFormat("Starting receiver socket on {0}:{1}...", IpAddressString, PortNumber);

                listener = new AdvUdpClient(PortNumber);
                CurrentIpAddress = IPAddress.Parse(IpAddressString);
                listener.JoinMulticastGroup(CurrentIpAddress, 5);
               
                Running = true;
                ReceiverThread = new Thread(Receive);
                ReceiverThread.Name = "MulticastReceiver";
                ReceiverThread.IsBackground = true;
                ReceiverThread.Start();

                return true;
            }
            return false;
        }

        public override bool Stop()
        {
            if (base.Stop())
            {
                listener.Close();

                return true;
            }
            return false;
        }

        #endregion

        private void Receive()
        {
            logger.Info("...receiver socket waiting");
            while (Running)
            {
                Thread.Sleep(100);
                IPEndPoint callerEndpoint = null;
                byte[] buffer = listener.Receive(ref callerEndpoint);

                string str = System.Text.Encoding.ASCII.GetString(buffer, 0, buffer.Length);
                MulticastMessage message = Helper.ConvertInput(buffer);
                FireMulticastMessageReceived(message);
            }
        }

        public IList<IMulticastMessageInterested> MulticastMessageConsumer { get; set; }

        public void AddMulticastMessageConsumer(IMulticastMessageInterested consumer)
        {
            if (!MulticastMessageConsumer.Contains(consumer))
                MulticastMessageConsumer.Add(consumer);
        }

        private void FireMulticastMessageReceived(MulticastMessage message)
        {
            lock (MulticastMessageConsumer)
            {
                foreach (var listener in MulticastMessageConsumer)
                {
                    listener.MulticastMessageArrived(message);
                }
            }
        }
    }

    class AdvUdpClient : UdpClient
    {
        public AdvUdpClient(int port)
        {
            this.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
            this.Client.Bind(new IPEndPoint(IPAddress.Any, port));
        }
    }
}
